package com.ruoyi.quartz.task;

import com.google.common.util.concurrent.*;
import com.ruoyi.bizsys.domain.BatchJobMonitoring;
import com.ruoyi.bizsys.domain.CreditAccount;
import com.ruoyi.bizsys.domain.LoanIou;
import com.ruoyi.bizsys.domain.LoanOffset;
import com.ruoyi.bizsys.service.IBatchJobMonitoringService;
import com.ruoyi.bizsys.service.ILoanIouService;
import com.ruoyi.bizsys.service.ILoanOffsetService;
import com.ruoyi.business.processIou.ProcessForIouService;
import com.ruoyi.business.processIou.ProcessForIouServiceForExceptionData;
import com.ruoyi.business.service.IBLoanInfoService;
import com.ruoyi.business.service.IBatchDateJobService;
import com.ruoyi.business.service.impl.BLoanInfoServiceImpl;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.common.config.ServerYml;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.quartz.aop.BathJobAspect;
import com.ruoyi.quartz.thread.BatchJobThread;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 批量任务
 *
 * @author ruoyi
 */
@Slf4j
@Component("batchJob")
public class BatchJob {

    @Autowired
    IBatchDateJobService batchDateJobService;

    @Autowired
    @Lazy
    IBLoanInfoService loanInfoService;

    @Autowired
    IBatchJobMonitoringService batchJobMonitoringService;

    @Autowired
    RedisCache redisCache;

    @Autowired
    BathJobAspect bathJobAspect;

    @Autowired
    private ProcessForIouService processForIouService;

    @Autowired
    private ProcessForIouServiceForExceptionData processForIouServiceForExceptionData;

    @Autowired
    ILoanOffsetService loanOffsetService;


    /**
     * 单个数据
     *
     * @param custNo
     */
    public void runBatch(String custNo) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date nowDate = calendar.getTime();
        loanInfoService.doBatchJobByCust(nowDate, loanInfoService.getOneOnLoanAccountAll(DateUtils.dateTime(nowDate), custNo));
    }

    /**
     * 处理异常数据 LateFee 改成-1即可重新回溯
     */
    public void runBatchForIou() {
        // LateFee = -1的
        List<LoanOffset> list = loanOffsetService.selectLoanOffsetListByLateFee();
        log.info("处理异常数据offset_:list.size:{}", list.size());
        if (!CollectionUtils.isEmpty(list)) {
            for (int i = 0; i < list.size(); i++) {
                log.info("处理异常数据offset_:getLoanSerialNo:{}", list.get(i).getLoanSerialNo());
                processForIouServiceForExceptionData.initForOne(list.get(i).getLoanSerialNo());
            }
        }
    }

    public void runBatch() {

        log.info("每日跑批任务 Start>>>>>>>>>>");
        boolean result = true;
        Date nowBatchDate = new Date();

        //获取系统最近跑批
        BatchJobMonitoring batchJobMonitoring = batchDateJobService.getBatchDateRecord();
        if (null == batchJobMonitoring) {
            log.info("最近跑批记录为 null");
            throw new BusinessException(RspEnmu.SYSTEM_SYSDATA_EXCEPTION);
        }

        log.info("最近跑批日为{}", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, batchJobMonitoring.getBatchDate()));
        Date batchDate = batchJobMonitoring.getBatchDate();

        // 跑批日相差
        int dayDiff = DateUtils.daysBetween(batchDate, nowBatchDate);
        if (dayDiff != 1) {
            log.info("最近一次跑批时间不是昨天");
            throw new BusinessException(RspEnmu.SYSTEM_SYSDATA_EXCEPTION);
        }

        //开始跑批
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(nowBatchDate);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date nowDate = calendar.getTime();

        //查询所有在贷的账户总数(在贷激活日早于跑批日的)
        //int accountCount = loanInfoService.getBatchJobOnLoanAccountNum(DateUtils.dateTime(nowDate));
        List<CreditAccount> accountList = loanInfoService.getBatchJobOnLoanAccount(DateUtils.dateTime(nowDate));
        int accountCount = accountList.size();
        redisCache.setCacheObject(BLoanInfoServiceImpl.theardName + BizConstants.MONITOR_COUNTNUMS, accountCount);
        log.info("日切跑批共有 {} 个客户需要执行", accountCount);
        if (accountCount > 0) {
            String redisName = "BatchJob-" + DateUtils.dateTime(nowDate);

            int threadStep = 200;//每个线程执行的客户数
            //需要multiple个线程来执行
            int threadCount = accountCount / threadStep;
            if (threadCount * threadStep != accountCount) {
                threadCount++;
            }
            //线程计数器
            final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
            //线程池大小
            int poolSize = 5;

            ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
            ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);

            int threadAccountCount = 0;
            while (threadAccountCount != accountCount) {
                //分页取数startNum
                int startLimit = threadAccountCount;

                threadAccountCount += threadStep;

                if (threadAccountCount > accountCount) {
                    threadAccountCount = accountCount;
                }

                String redisName1 = redisName + ":" + startLimit + "-" + threadAccountCount;
                List<CreditAccount> accountList1 = accountList.subList(startLimit, threadAccountCount);
                redisCache.setCacheList(redisName1, accountList1);
                redisCache.expire(redisName1, 23, TimeUnit.HOURS);

                BatchJobThread thread = new BatchJobThread("[BatchJobThread-" + startLimit + "]", nowDate, redisName1,
                        loanInfoService, redisCache);
                ListenableFuture listenableFuture = listeningExecutorService.submit(thread);

                Futures.addCallback(listenableFuture, new FutureCallback<String>() {
                    @Override
                    public void onSuccess(String result) {
                        countDownLatch.countDown();
                        log.info("[" + this.getClass().getName() + "]线程处理结果：" + result);
                    }

                    @Override
                    public void onFailure(Throwable throwable) {
                        countDownLatch.countDown();
                        log.info("[" + this.getClass().getName() + "]线程处理出错：" + throwable);
                    }
                });
            }

            try {
                countDownLatch.await(3 * 60, TimeUnit.MINUTES);//超过3个小时放弃
            } catch (InterruptedException e) {
                result = false;
                log.error("当日跑批执行失败，非正常结束", e);
            }
        } else {
            log.info("每日跑批任务没有记录需要执行");
        }

        //日切
        if (result) {
            batchJobMonitoring.setBatchDate(nowBatchDate);
            batchJobMonitoringService.updateBatchJobMonitoring(batchJobMonitoring);
        }

        log.info("每日跑批任务 End>>>>>>>>>>");
    }
}

